package com.gzucm.Scholarship.common.base;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.Predicate;
import java.util.*;

@Transactional
public class BaseServiceImpl<T extends BaseEntity> implements BaseService<T> {

    private BaseDao<T, String> dao;

    public BaseServiceImpl(BaseDao<T, String> dao) {
        this.dao = dao;
    }

    @Override
    public T save(T entity) {
        return dao.save(entity);
    }

    @Override
    public List<T> save(Iterable<T> iterable) {
        return dao.saveAll(iterable);
    }

    @Override
    public void delete(T entity) {
        dao.delete(entity);
    }

    @Override
    public void delete(String id) {
        dao.deleteById(id);
    }

    @Override
    public void delete(Iterable<T> iterable) {
        dao.deleteInBatch(iterable);
    }

    @Override
    public void delete(String... ids) {
        for (String id : ids) {
            delete(id);
        }
    }

    @Override
    public void logicalDelete(T entity) {
        ((BaseEntity) entity).setDeleteflag(true);
        save(entity);
    }

    @Override
    public void logicalDelete(String id) {
        T entity = findById(id);
        if (Objects.nonNull(entity)) {
            logicalDelete(entity);
        }
    }

    @Override
    public void logicalDelete(Iterable<T> iterable) {
        Iterator<T> iterator = iterable.iterator();
        while (iterator.hasNext()) {
            logicalDelete(iterator.next());
        }
    }

    @Override
    public void logicalDelete(String... ids) {
        logicalDelete(findByIds(Arrays.asList(ids)));
    }

    @Override
    public T findById(String id) {
        return dao.findById(id).orElse(null);
    }

    @Override
    public List<T> findAll() {
        return dao.findAll();
    }

    @Override
    public Page<T> listByPage(Pageable pageable) {
        return dao.findAll(pageable);
    }

    @Override
    public Page<T> listByPage(Specification<T> spec, Pageable pageable) {
        return dao.findAll(spec, pageable);
    }

    @Override
    public Long count() {
        return dao.count();
    }

    @Override
    public Boolean existsById(String id) {
        return dao.existsById(id);
    }

    @Override
    public Long count(Specification spec) {
        return dao.count(spec);
    }

    @Override
    public Page<T> listByPage(Map<String, Object> params, Pageable pageable) {
        return listByPage(getSpec(params), pageable);
    }

    @Override
    public List<T> findAll(Map<String, Object> params) {
        return dao.findAll(getSpec(params));
    }

    @Override
    public List<T> findByIds(Iterable<String> iterable) {
        return dao.findAllById(iterable);
    }

    private Specification<T> getSpec(Map<String, Object> params) {
        return (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicateList = new ArrayList<>();
            params.keySet().forEach(key -> criteriaBuilder.equal(root.get(key), params.get(key)));
            return criteriaBuilder.and(predicateList.toArray(new Predicate[0]));
        };
    }
}
